package jmine.tec.utils.collection;

import java.io.Serializable;

/**
 * Um mapper que faz a 'funcao composta' entre 2 outros mappers
 * 
 * @author takeshi
 * @param <I> o input
 * @param <O> o output
 * @param <M> o resultado intermediario
 */
public class ChainingMapper<I, O, M> implements Mapper<I, O>, Serializable {

    private final Mapper<I, M> first;

    private final Mapper<M, O> second;

    /**
     * C'tor
     * 
     * @param first the first
     * @param second the second
     */
    public ChainingMapper(Mapper<I, M> first, Mapper<M, O> second) {
        super();
        if (first == null || second == null) {
            throw new NullPointerException("mapper cannot be null");
        }
        this.first = first;
        this.second = second;
    }

    /**
     * {@inheritDoc}
     */
    public O apply(I input) {
        return this.second.apply(this.first.apply(input));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "Chaining " + second + " ( " + first + " )";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.first == null) ? 0 : this.first.hashCode());
        result = prime * result + ((this.second == null) ? 0 : this.second.hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        ChainingMapper other = (ChainingMapper) obj;
        if (this.first == null) {
            if (other.first != null) {
                return false;
            }
        } else if (!this.first.equals(other.first)) {
            return false;
        }
        if (this.second == null) {
            if (other.second != null) {
                return false;
            }
        } else if (!this.second.equals(other.second)) {
            return false;
        }
        return true;
    }

}
